﻿using System;
using System.Collections;
using System.Linq;
using Kaesoft.Hospedaje.Entities;
using Kaesoft.Hospedaje.Data;
using System.Configuration;

namespace Kaesoft.Hospedaje.Business
{
    public static class EntidadesUtil
    {
        #region Metodos Publicos
        public static int PageSize = ConfigurationManager.AppSettings["PageSize"] != null ? int.Parse(ConfigurationManager.AppSettings["PageSize"].ToString()) : 10;

        /// <summary>
        /// Setear datos de auditoria en la entidad
        /// </summary>
        public static void SetCamposAuditoria(ReservacionesEntities ctx, IAuditable entidad, AuditData auditData)
        {
            if (auditData == null) throw new ArgumentNullException("auditData");
            if (entidad == null) throw new ArgumentNullException("entidad");

            if (ctx != null)
            {
                ctx.Usuario = auditData.Usuario;
                ctx.Ip = auditData.Ip;
            }

            switch (((IObjectWithChangeTracker)entidad).ChangeTracker.State)
            {
                case ObjectState.Added:
                    SetCamposAuditoriaNuevo(entidad, auditData);
                    break;

                case ObjectState.Modified:
                    SetCamposComunAuditoria(entidad, auditData);
                    break;

                case ObjectState.Deleted:
                    SetCamposComunAuditoria(entidad, auditData);
                    entidad.borrado = true;
                    break;

                default:
                    break;
            }

            //Setea los datos de auditoría para las colecciones internas
            foreach (var property in entidad.GetType().GetProperties().Where(p => p.PropertyType.Name.Contains("TrackableCollection")))
            {
                IEnumerable collection = (IEnumerable)property.GetValue(entidad, null);
                if (collection != null)
                    foreach (var item in collection)
                        SetCamposAuditoria(null, (IAuditable)item, auditData);
            }
        }

        /// <summary>
        /// Setear datos de auditoria en la entidad
        /// </summary>
        /// <param name="entidad">Entidad</param>
        /// <param name="auditData">Datos de Auditoría</param>
        /// <param name="tieneCollecciones">Setear o no los datos de auditoria en colecciones hijas</param>
        /// <param name="setClavePrimaria">Setear o no un valor en la clave primaria (falso cuando hay multiple clave primaria)</param>
        /// <param name="nombreClave">Nombre de la clave primaria</param>
        public static void SetCamposAuditoria(ReservacionesEntities ctx, IAuditable entidad, AuditData auditData, bool tieneCollecciones = true,
                                              bool setClavePrimaria = false, string nombreClave = null)
        {
            if (auditData == null) throw new ArgumentNullException("auditData");
            if (entidad == null) throw new ArgumentNullException("entidad");

            if (ctx != null)
            {
                ctx.Usuario = auditData.Usuario;
                ctx.Ip = auditData.Ip;
            }

            switch (((IObjectWithChangeTracker)entidad).ChangeTracker.State)
            {
                case ObjectState.Added:
                    SetCamposAuditoriaNuevo(entidad, auditData, setClavePrimaria, nombreClave);
                    break;

                case ObjectState.Modified:
                    SetCamposComunAuditoria(entidad, auditData);
                    break;

                case ObjectState.Deleted:
                    SetCamposComunAuditoria(entidad, auditData);
                    entidad.borrado = true;
                    break;

                default:
                    break;
            }

            //Si es necesario llenar datos de auditoria en colecciones
            if (tieneCollecciones)
            {
                foreach (
                    var property in
                        entidad.GetType().GetProperties().Where(p => p.PropertyType.Name.Contains("TrackableCollection")))
                {
                    IEnumerable collection = (IEnumerable)property.GetValue(entidad, null);
                    if (collection != null)
                        foreach (var item in collection)
                            SetCamposAuditoria(null, (IAuditable)item, auditData, tieneCollecciones, setClavePrimaria, nombreClave);
                }
            }
        }
        #endregion

        #region Metodos Privados
        /// <summary>
        /// Datos de auditoría comunes
        /// </summary>
        /// <param name="entidad">Entidad</param>
        /// <param name="auditData">Datos de auditoría</param>
        private static void SetCamposComunAuditoria(IAuditable entidad, AuditData auditData)
        {
            entidad.usuarioModificacion = auditData.Usuario;
            entidad.fechaModificacion = DateTime.Now;
            entidad.programaModificacion = auditData.Programa;
        }

        /// <summary>
        /// Setea los campos de auditoría para una nueva instancia
        /// </summary>
        /// <param name="entidad">Entidad</param>
        /// <param name="auditData">Datos de auditoría</param>
        private static void SetCamposAuditoriaNuevo(IAuditable entidad, AuditData auditData)
        {
            var nombreEntidad = entidad.GetType().ToString().Split('.');
            string nombreEntidadId = "id" + nombreEntidad[nombreEntidad.Length - 1];
            var idEntidad = entidad.GetType().GetProperty(nombreEntidadId);

            if (idEntidad != null)
                idEntidad.SetValue(entidad, Guid.NewGuid(), null);

            entidad.usuarioCreacion = auditData.Usuario;
            entidad.fechaCreacion = DateTime.Now;

            entidad.borrado = false;
            entidad.programaCreacion = auditData.Programa;
        }

        /// <summary>
        /// Setear datos de auditoria en la entidad
        /// </summary>
        /// <param name="entidad">Entidad</param>
        /// <param name="auditData">Datos de auditoría</param>
        /// <param name="setClavePrimaria">Setear o no un valor en la clave primaria (falso cuando hay multiple clave primaria)</param>
        /// <param name="nombreClave">Nombre de la clave primaria</param>
        private static void SetCamposAuditoriaNuevo(IAuditable entidad, AuditData auditData, bool setClavePrimaria, string nombreClave)
        {
            if (setClavePrimaria)
            {
                var nombreEntidad = entidad.GetType().ToString().Split('.');
                string entidadId = nombreClave ?? ("id" + nombreEntidad[nombreEntidad.Length - 1]);
                if (entidadId == null) throw new NotImplementedException();
                var id = entidad.GetType().GetProperties().FirstOrDefault(p => p.Name == entidadId);
                if (id != null)
                    entidad.GetType().GetProperty(entidadId).SetValue(entidad, Guid.NewGuid(), null);
            }

            entidad.usuarioCreacion = auditData.Usuario;
            entidad.fechaCreacion = DateTime.Now;
            entidad.programaCreacion = auditData.Programa;
            entidad.borrado = false;
        }
        #endregion
    }
}